﻿using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;

namespace RangeFinder
{
    /// <summary>
    /// Program used to test and tweak the breadth-first-algorithm that is used to 
    /// find the reachable tiles on the level in TacticalGridWars
    /// </summary>
    public partial class RangeFinder : Form
    {

        private Graphics g;
        private Brush brushWall = new SolidBrush(Color.Blue);
        private Brush brushCover = new SolidBrush(Color.Cyan);
        private Brush brushSpawn1 = new SolidBrush(Color.Green);
        private Brush brushSpawn2 = new SolidBrush(Color.Purple);
        private Pen pen = new Pen(new SolidBrush(Color.Black));

        public enum TileState { Empty, Cover, Wall, Team1Spawn, Team2Spawn }

        public enum Dir { Up, Down, Left, Right, UpLeft, UpRight, DownLeft, DownRight, None }

        public class Tile
        {
            public TileState State { get; set; }
            public Dir ParentDir { get; set; }
            public int Moves { get; set; }
            //public int X { get; set; }
            //public int Y { get; set; }

            public Tile()
            {
                State = TileState.Empty;
                ParentDir = Dir.None;
                //X = Y = 
                Moves = 0;
            }
        }

        const int tileSize = 30;

        private Tile[,] level;
        private int moves = 2;
        //List<Tile> que = new List<Tile>();
        //List<Tile> visited = new List<Tile>();
        private List<Tuple<int, int>> que = new List<Tuple<int, int>>();
        private List<Tile> visited = new List<Tile>();

        public RangeFinder()
        {
            InitializeComponent();
            level = new Tile[10,10];
            for (int i = 0; i < 10; i++)
            {
                for (int j = 0; j < 10; j++)
                {
                    level[j, i] = new Tile();
                    //level[j, i].X = i;
                    //level[j, i].Y = j;
                }
            }
            level[3, 3].State = TileState.Wall;
            level[3, 4].State = TileState.Wall;
            level[3, 5].State = TileState.Wall;
            level[3, 6].State = TileState.Wall;
            level[4, 3].State = TileState.Wall;
            level[4, 6].State = TileState.Wall;
            level[5, 3].State = TileState.Wall;
            level[5, 6].State = TileState.Wall;
            level[6, 3].State = TileState.Wall;
            level[6, 4].State = TileState.Wall;
            level[6, 6].State = TileState.Wall;
        g = this.CreateGraphics();
        }

        private Dir[] GetDirsToVisit(Dir parentDir)
        {
            switch (parentDir)
            {
                case Dir.Up:
                    return new Dir[] { Dir.Up, Dir.UpLeft, Dir.UpRight };
                case Dir.Down:
                    return new Dir[] { Dir.Down, Dir.DownLeft, Dir.DownRight };
                case Dir.Left:
                    return new Dir[] { Dir.Left, Dir.UpLeft, Dir.DownLeft };
                case Dir.Right:
                    return new Dir[] { Dir.Right, Dir.UpRight, Dir.DownRight };
                case Dir.UpLeft:
                    return new Dir[] { Dir.Up, Dir.Left, Dir.UpLeft, Dir.UpRight, Dir.DownLeft };
                case Dir.UpRight:
                    return new Dir[] { Dir.Up, Dir.Right, Dir.UpLeft, Dir.UpRight, Dir.DownRight };
                case Dir.DownLeft:
                    return new Dir[] { Dir.Down, Dir.Left, Dir.UpLeft, Dir.DownLeft, Dir.DownRight };
                case Dir.DownRight:
                    return new Dir[] { Dir.Down, Dir.Right, Dir.UpRight, Dir.DownLeft, Dir.DownRight };
                default:
                    return new Dir[] { Dir.Up, Dir.Down, Dir.Left, Dir.Right, Dir.UpLeft, Dir.UpRight, Dir.DownLeft, Dir.DownRight };
            }
        }

        private void SearchReachable(int x, int y, int moves)
        {
            int walkLimit;
            int sprintLimit;
            if (moves > 1)
            {
                walkLimit = 4;
                sprintLimit = 2;
            }
            else
            {
                walkLimit = 2;
                sprintLimit = 0;
            }
            que.Clear();
            visited.Clear();
            level[y, x].Moves = 0;
            level[y, x].ParentDir = Dir.None;
            visited.Add(level[y,x]);
            que.Add(new Tuple<int, int>(x, y));
            while(que.Count != 0)
            {
                Dir[] dirs = GetDirsToVisit(level[que.Last().Item2, que.Last().Item1].ParentDir);
                foreach (var dir in dirs)
                {
                    VisitNeighbour(que.Last(), dir, walkLimit, sprintLimit);
                }
                que.Remove(que.Last());
            }
            this.Invalidate();
        }

        private void VisitNeighbour(Tuple<int, int> coords, Dir dir, int walkLimit, int sprintLimit)
        {
            int x;
            int y;
            switch (dir)
            {
                case Dir.Up: x = 0; y = -1; break;
                case Dir.Down: x = 0; y = 1; break;
                case Dir.Left: x = -1; y = 0; break;
                case Dir.Right: x = 1; y = 0; break;
                case Dir.UpLeft: x = -1; y = -1; break;
                case Dir.UpRight: x = 1; y = -1; break;
                case Dir.DownLeft: x = -1; y = 1; break;
                case Dir.DownRight: x = 1; y = 1; break;
                default: x = 0; y = 0; break;
            }
            x += coords.Item1;
            y += coords.Item2;

            if (x >= 0 && x < 10 && y >= 0 && y < 10 && !visited.Contains(level[y, x]))
            {
                if (level[y,x].State == TileState.Empty)
                {
                    level[y, x].Moves = level[coords.Item2, coords.Item1].Moves + 1;
                    level[y, x].ParentDir = dir;
                    if (level[y,x].Moves < walkLimit)
                    {
                        level[y, x].State = TileState.Team1Spawn;
                        //g.FillRectangle(brushSpawn1, x * tileSize, y * tileSize, tileSize, tileSize);//debug
                    }
                    else if (level[y, x].Moves < (walkLimit + sprintLimit))
                    {
                        level[y, x].State = TileState.Team2Spawn;
                        //g.FillRectangle(brushSpawn2, x * tileSize, y * tileSize, tileSize, tileSize);//debug
                    }
                    if (level[y, x].Moves < (walkLimit + sprintLimit - 1))
                    {
                        que.Insert(0, new Tuple<int, int>(x, y));
                    }
                }
                visited.Add(level[y, x]);
            }
            //System.Threading.Thread.Sleep(250);//debug
        }

        private void RangeFinder_Paint(object sender, PaintEventArgs e)
        {

            for (int i = 0; i < 10; i++)
            {
                for (int j = 0; j < 10; j++)
                {
                    if (level[i, j].State == TileState.Cover)
                    {
                        g.FillRectangle(brushCover, j * tileSize, i * tileSize, tileSize, tileSize);
                    }
                    else if (level[i, j].State == TileState.Wall)
                    {
                        g.FillRectangle(brushWall, j * tileSize, i * tileSize, tileSize, tileSize);
                    }
                    else if (level[i, j].State == TileState.Team1Spawn)
                    {
                        g.FillRectangle(brushSpawn1, j * tileSize, i * tileSize, tileSize, tileSize);
                    }
                    else if (level[i, j].State == TileState.Team2Spawn)
                    {
                        g.FillRectangle(brushSpawn2, j * tileSize, i * tileSize, tileSize, tileSize);
                    }
                    g.DrawRectangle(pen, j * tileSize, i * tileSize, tileSize, tileSize);
                }
            }
        }

        private void RangeFinder_MouseClick(object sender, MouseEventArgs e)
        {
            int x = e.X / tileSize;
            int y = e.Y / tileSize;
            SearchReachable(x, y, moves);
            if (moves == 2)
            {
                moves = 1;
            }
            else
            {
                moves = 2;
            }
        }

        private void RangeFinder_KeyDown(object sender, KeyEventArgs e)
        {
            if (e.KeyCode == Keys.Escape)
            {
                for (int i = 0; i < 10; i++)
                {
                    for (int j = 0; j < 10; j++)
                    {
                        level[j, i].State = TileState.Empty;
                        //level[j, i].X = i;
                        //level[j, i].Y = j;
                    }
                }
            }
            level[3, 3].State = TileState.Wall;
            level[3, 4].State = TileState.Wall;
            level[3, 5].State = TileState.Wall;
            level[3, 6].State = TileState.Wall;
            level[4, 3].State = TileState.Wall;
            level[4, 6].State = TileState.Wall;
            level[5, 3].State = TileState.Wall;
            level[5, 6].State = TileState.Wall;
            level[6, 3].State = TileState.Wall;
            level[6, 4].State = TileState.Wall;
            level[6, 6].State = TileState.Wall;
            this.Invalidate();
        }
    }
}
